
import { _decorator, Component, director, Node } from 'cc';
import { TileManager } from '../Tile/TileManager';
import { createUINode } from '../../Utils';
const { ccclass, property } = _decorator;
import levels, { ILevel, ITile } from 'db://assets/Levels'
import { DataManager } from '../../RunTime/DataManager';
import { TILE_HEIGHT, TILE_WEIGHT } from '../Tile/TileItem';
import { EventManager } from '../../RunTime/EventManager';
import { DIRECTION_ENUM, ENTITY_STATE_ENUM, ENTITY_TYPE_ENUM, EVENT_ENUM } from '../../Enum';
import { PlayerManager } from '../Player/PlayerManager';
import { WoodenSkeletonManager } from '../WoodenSkeleton/WoodenSkeletonManager';
import { DoorManager } from '../Door/DoorManager';
import { IronSkeletonManager } from '../IronSkeleton/IronSkeletonManager';
import { BurstManager } from '../Burst/BurstManager';
import { SpikesManager } from '../Spikes/SpikesManager';
import { SmokeManager } from '../Smoke/SmokeManager';
import { FadeManager } from '../../RunTime/FadeManager';
import { ShakeManager } from '../../RunTime/ShakeManager';

@ccclass('BattleManager')
export class BattleManager extends Component {

	level: ILevel
	stage: Node
	smokeLayer: Node
	isInited: boolean = false

	onLoad(): void {
		const eventManager = EventManager.getInstance()
		eventManager.on(EVENT_ENUM.NEXT_LEVEL, this.nextLevel, this)
		eventManager.on(EVENT_ENUM.PLAYER_MOVE_END, this.OnPlayerMove, this)
		eventManager.on(EVENT_ENUM.SHOW_SMOKE, this.generateSmoke, this)
		eventManager.on(EVENT_ENUM.RECORD_STEP, this.record, this)
    	eventManager.on(EVENT_ENUM.REVOKE_STEP, this.revoke, this)
    	eventManager.on(EVENT_ENUM.RESTART, this.restart, this)
    	eventManager.on(EVENT_ENUM.QUIT, this.quit, this)
	}

	onDestroy(): void {
		const eventManager = EventManager.getInstance()
		eventManager.off(EVENT_ENUM.NEXT_LEVEL, this.nextLevel)
		eventManager.off(EVENT_ENUM.PLAYER_MOVE_END, this.OnPlayerMove)
		eventManager.off(EVENT_ENUM.SHOW_SMOKE, this.generateSmoke)
		eventManager.off(EVENT_ENUM.RECORD_STEP, this.record)
    	eventManager.off(EVENT_ENUM.REVOKE_STEP, this.revoke)
		eventManager.off(EVENT_ENUM.RESTART, this.restart)
    	eventManager.off(EVENT_ENUM.QUIT, this.quit)
	}

	async start() {
		this.generateStage()
		await this.initLevels()
	}

	async OnPlayerMove() {
		const { door, player } = DataManager.getInstance()
		if (door.x == player.x && door.y == player.y) {
			await this.nextLevel()
		}
	}


	generateStage() {
		this.stage = createUINode()
		this.stage.setParent(this.node)
		this.stage.addComponent(ShakeManager)
	}

	async nextLevel() {
		DataManager.getInstance().level++
		this.stage.destroyAllChildren()
		await this.initLevels()
	}

	async initLevels() {
		DataManager.resetData()
		const DataManagerInstance = DataManager.getInstance()
		const levelInfo = levels[`level${DataManagerInstance.level}`]
		if (levelInfo) {
			this.level = levelInfo
			DataManagerInstance.mapInfo = this.level.mapInfo
			DataManagerInstance.mapRowCount = this.level.mapInfo.length || 0
			DataManagerInstance.mapColumnCount = this.level.mapInfo[0].length || 0
			if (this.isInited){
				await FadeManager.getInstance().fadeIn()
			}else{
				FadeManager.getInstance().mask()
			}
			await Promise.all([
				this.generateTitleMap(),
				this.initEnemies(),
				this.initDoor(),
				this.initBursts(),
				this.initSpikes(),
				this.generateSmokeLayer(),
			])
			await this.initPlayer()
			await FadeManager.getInstance().fadeOut()
			this.isInited = true
		} else{
			director.loadScene('Start')
		}
	}

	async generateTitleMap() {
		const tileMap = createUINode()
		tileMap.setParent(this.stage)
		const tileManager = tileMap.addComponent(TileManager)
		await tileManager.init()

		this.adaptPos()

	}


	adaptPos() {
		const { mapRowCount, mapColumnCount } = DataManager.getInstance()
		const disX = TILE_WEIGHT * mapRowCount / 2
		const disY = TILE_HEIGHT * mapColumnCount / 2 + 80
		this.stage.setPosition(-disX, disY)
	}

	async initPlayer() {
		const player = this.level.player
		const playerNode = createUINode()
		playerNode.setParent(this.stage)
		const playerManager = playerNode.addComponent(PlayerManager)
		await playerManager.init(player)
		DataManager.getInstance().player = playerManager
	}

	async initEnemies() {
		const { enemys } = DataManager.getInstance()
		for (let i = 0; i < this.level.enemies.length; i++) {
			const enemyNode = createUINode()
			enemyNode.setParent(this.stage)
			const enemy = this.level.enemies[i]
			const Manager = enemy.type == ENTITY_TYPE_ENUM.SKELETON_WOODEN ? WoodenSkeletonManager : IronSkeletonManager
			const enemyManager = enemyNode.addComponent(Manager)
			enemyManager.id = i
			await enemyManager.init(enemy)
			enemys.push(enemyManager)
		}

	}

	async initDoor() {
		const doorNode = createUINode()
		doorNode.setParent(this.stage)
		const doorManager = doorNode.addComponent(DoorManager)
		await doorManager.init(this.level.door)
		DataManager.getInstance().door = doorManager
	}

	async initBursts() {
		for (let i = 0; i < this.level.bursts.length; i++) {
			const burst = this.level.bursts[i]
			const burstNode = createUINode()
			burstNode.setParent(this.stage)
			const burstManager = burstNode.addComponent(BurstManager)
			await burstManager.init(burst)
			DataManager.getInstance().bursts.push(burstManager)
		}

	}

	async initSpikes() {
		for (let i = 0; i < this.level.spikes.length; i++) {
			const spike = this.level.spikes[i]
			const spikeNode = createUINode()
			spikeNode.setParent(this.stage)
			const spikesManager = spikeNode.addComponent(SpikesManager)
			await spikesManager.init(spike)
		}
	}

	async generateSmokeLayer() {
		this.smokeLayer = createUINode()
		this.smokeLayer.setParent(this.stage)
	}

	async generateSmoke(x: number, y: number, direction: DIRECTION_ENUM) {
		const { smokes } = DataManager.getInstance()
		const item = smokes.find(somke => somke.state == ENTITY_STATE_ENUM.DEAD)
		if (item) {
			item.x = x
			item.y = y
			item.node.setPosition(x * TILE_WEIGHT - 1.5 * TILE_WEIGHT, -y * TILE_HEIGHT + 1.5 * TILE_HEIGHT)
			item.direction = direction
			item.state = ENTITY_STATE_ENUM.IDLE
		} else {
			const smokeNode = createUINode()
			smokeNode.setParent(this.smokeLayer)
			const smokeManager = smokeNode.addComponent(SmokeManager)
			const state = ENTITY_STATE_ENUM.IDLE
			const type = ENTITY_TYPE_ENUM.SMOKE
			await smokeManager.init({ x, y, direction, state, type })
			smokes.push(smokeManager)
		}
	}


	record() {
		const data = DataManager.getInstance()
		const record: Omit<ILevel, 'mapInfo'> = {
			player: {
				x: data.player.x,
				y: data.player.y,
				direction: data.player.direction,
				state: data.player.state,
				type: data.player.type
			},
			enemies: data.enemys.map(({ x, y, direction, state, type }) => {
				return { x, y, direction, state, type }
			}),
			bursts: data.bursts.map(({ x, y, direction, state, type }) => {
				return ({ x, y, direction, state, type })
			}),
			door: {
				x: data.door.x,
				y: data.door.y,
				direction: data.door.direction,
				state: data.door.state,
				type: data.door.type
			},
			spikes: data.spikes.map(spike => {
				return {
					x: spike.x,
					y: spike.y,
					totalCnt: spike.totalCnt,
					currentCnt: spike.currentCnt,
				}
			})
		}
		data.records.push(record)
	}

	revoke() {
		const data = DataManager.getInstance()
		const record = data.records.pop()

		if (!record){
			return 
		}

		const { player, enemies, bursts, door, spikes } = record


		data.player.x = data.player.targetX = player.x
		data.player.y = data.player.targetY = player.y
		data.player.direction = player.direction
		data.player.state = player.state
		data.player.type = player.type

		for (let i = 0; i < enemies.length; i++) {
			const enemy = enemies[i]
			data.enemys[i].x = enemy.x
			data.enemys[i].y = enemy.y
			data.enemys[i].direction = enemy.direction
			data.enemys[i].state = enemy.state
			data.enemys[i].type = enemy.type
		}

		for (let i = 0; i < bursts.length; i++) {
			const burst = bursts[i]
			data.bursts[i].x = burst.x
			data.bursts[i].y = burst.y
			data.bursts[i].direction = burst.direction
			data.bursts[i].state = burst.state
			data.bursts[i].type = burst.type
		}

		data.door.x = door.x
		data.door.y = door.y
		data.door.direction = door.direction
		data.door.state = door.state
		data.door.type = door.type

		for (let i = 0; i < spikes.length; i++) {
			const spike = spikes[i]
			data.spikes[i].x = spike.x
			data.spikes[i].y = spike.y
			data.spikes[i].totalCnt = spike.totalCnt
			data.spikes[i].currentCnt = spike.currentCnt
		}

	}

	async restart(){
		this.stage.destroyAllChildren()
		await this.initLevels()
	}

	quit(){
		director.loadScene('Start')
	}


}



